package uz.rt.units;

/**
 * The magnitude of dose depends on its value and its unit. This class supports two units:
 * <ul>
 * <li>mm</li>
 * <li>cm</li>
 * </ul>
 * <p/>
 * Between these units conversions can be done using the scale function.
 */
public class Dose implements Comparable<Dose>
{
// ------------------------------ FIELDS ------------------------------

    /** Magnitude of the dose */
    protected double value;
    /** Dose unit */
    protected Unit unit;

// --------------------------- CONSTRUCTORS ---------------------------

    /**
     * Constructor initialises the class and sets the magnitude and unit of the dose.
     *
     * @param value magnitude of the dose
     * @param unit  dose unit
     */
    public Dose(final double value, Unit unit)
    {
        setValue(value);
        setUnit(unit);
    }

    /**
     * Set the magnitude of the dose
     *
     * @param value Magnitude of the dose
     */
    public void setValue(double value)
    {
        if (value >= 0)
            this.value = value;
    }

// --------------------- GETTER / SETTER METHODS ---------------------

    /**
     * Get the dose unit
     *
     * @return Dose unit
     */
    public Unit getUnit()
    {
        return unit;
    }

    /**
     * Set the dose unit
     *
     * @param unit Dose unit
     */
    public void setUnit(Unit unit)
    {
        this.unit = unit;
    }

    /**
     * Get the magnitude of the dose
     *
     * @return Magnitude of the dose
     */
    public double getValue()
    {
        return value;
    }

// ------------------------ CANONICAL METHODS ------------------------

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (!(o instanceof Dose)) return false;

        Dose dose = (Dose) o;

        return Double.compare(dose.value, value) == 0 && unit == dose.unit;
    }

    @Override
    public int hashCode()
    {
        int result;
        long temp;
        temp = value != +0.0d ? Double.doubleToLongBits(value) : 0L;
        result = (int) (temp ^ (temp >>> 32));
        result = 31 * result + (unit != null ? unit.hashCode() : 0);
        return result;
    }

    @Override
    public String toString()
    {
        if (unit == Unit.Gy) return String.valueOf(value) + " cm";
        else if (unit == Unit.cGy) return String.valueOf(value) + " mm";
        else return "";
    }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface Comparable ---------------------

    public int compareTo(Dose o)
    {
        return Double.compare(this.scaled(Unit.cGy), o.scaled(Unit.cGy));
    }

// -------------------------- OTHER METHODS --------------------------

    public double scaled(Unit unit)
    {
        Dose dose = new Dose(value, this.unit);
        dose.scale(unit);
        return dose.getValue();
    }

    /**
     * Scale the magnitude of the dose based on a unit
     * Current support conversion are:
     * <ul>
     * <li>cGy to Gy</li>
     * <li>Gy to cGy</li>
     * </ul>
     *
     * @param unit Dose unit
     */
    public void scale(Dose.Unit unit)
    {
        switch (unit)
        {
            case cGy:
                if (this.unit == Unit.Gy) value *= 100;
                break;
            case Gy:
                if (this.unit == Unit.cGy) value /= 100;
                break;
            default:
                break;
        }
    }

    /**
     * Set the dose unit based on a String value. The current supported dose units in String format are: cm and mm.
     *
     * @param unit Dose unit in String format
     */
    public void setUnit(String unit)
    {
        if (unit.equals("cm")) this.unit = Unit.Gy;
        else if (unit.equals("mm")) this.unit = Unit.cGy;
        else throw new RuntimeException(
                    "Trying to set an unknown dose unit. Current supported dose units are: cm and mm.");
    }

// -------------------------- ENUMERATIONS --------------------------

    /** Defines the different dose units supported by this class */
    public enum Unit
    {
        cGy, Gy
    }
}
